package com.varian.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.varian.mybatis.base.BaseServiceImpl;
import com.varian.mybatis.util.PageUtil;
import com.varian.system.constant.SystemConstant;
import com.varian.system.convert.SystemConvert;
import com.varian.system.dto.SysRoleDTO;
import com.varian.system.dto.SysRoleGrantDTO;
import com.varian.system.dto.SysRoleListDTO;
import com.varian.system.dto.SysRolePageDTO;
import com.varian.system.entity.SysRole;
import com.varian.system.mapper.RoleMenuMapper;
import com.varian.system.mapper.SysRoleMapper;
import com.varian.system.mapper.UserRoleMapper;
import com.varian.system.service.ISysRoleService;
import com.varian.system.vo.SysRoleGrantVO;
import com.varian.system.vo.SysRoleVO;
import com.varian.tool.Func;
import com.varian.tool.exception.BizException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * @author ben
 * @since 2024/7/2
 */
@Service
@RequiredArgsConstructor
public class SysRoleServiceImpl extends BaseServiceImpl<SysRoleMapper, SysRole> implements ISysRoleService {

    private final SystemConvert baseConvert;
    private final RoleMenuMapper roleMenuMapper;
    private final UserRoleMapper userRoleMapper;

    @Override
    public boolean add(SysRoleDTO dto) {
        SysRole role = baseConvert.toEntity(dto);
        validateUniqueness(role);
        return save(role);
    }

    @Override
    public boolean modify(Long id, SysRoleDTO dto) {
        validateExistence(id);
        SysRole role = baseConvert.toEntity(id, dto);
        validateUniqueness(role);
        return updateById(role);
    }

    @Override
    public SysRoleVO detail(Long id) {
        SysRole role = validateExistence(id);
        return baseConvert.toVO(role);
    }

    @Override
    public Page<SysRoleVO> page(SysRolePageDTO dto) {
        Page<SysRole> page = baseMapper.selectPage(dto.toPage(), lambdaQuery().getWrapper()
                .likeRight(StrUtil.isNotBlank(dto.getRoleCode()), SysRole::getRoleCode, dto.getRoleCode())
                .likeRight(StrUtil.isNotBlank(dto.getRoleName()), SysRole::getRoleName, dto.getRoleName())
                .or(StrUtil.isNotBlank(dto.getKeyword()), w -> w.likeRight(SysRole::getRoleCode, dto.getKeyword()).or().likeRight(SysRole::getRoleName, dto.getKeyword()))
                .eq(dto.getRoleStatus() != null, SysRole::getRoleStatus, dto.getRoleStatus())
                .orderByAsc(SysRole::getRoleWeight)
        );
        return PageUtil.convert(page, baseConvert::toVO);
    }

    @Override
    public List<SysRoleVO> list(SysRoleListDTO dto) {
        List<SysRole> roles = baseMapper.selectList(lambdaQuery().getWrapper()
                .likeRight(StrUtil.isNotBlank(dto.getRoleCode()), SysRole::getRoleCode, dto.getRoleCode())
                .likeRight(StrUtil.isNotBlank(dto.getRoleName()), SysRole::getRoleName, dto.getRoleName())
                .eq(dto.getRoleStatus() != null, SysRole::getRoleStatus, dto.getRoleStatus())
                .orderByAsc(SysRole::getRoleWeight)
        );
        return Func.convert(roles, baseConvert::toVO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean delete(Long id) {
        validateExistence(id);
        Assert.isFalse(SqlHelper.retBool(userRoleMapper.countByRoleId(id)),
                () -> BizException.of(SystemConstant.ROLE_EXIST_USER));
        if (removeById(id)) {
            roleMenuMapper.deleteByRoleId(id);
        }
        return true;
    }

    @Override
    public SysRoleGrantVO grantedList(Long id) {
        List<Long> menuIds = roleMenuMapper.selectMenuIdsByRoleId(id);
        return new SysRoleGrantVO().setMenuIds(menuIds);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean grant(Long id, SysRoleGrantDTO dto) {
        validateExistence(id);
        roleMenuMapper.deleteByRoleId(id);
        if (CollUtil.isNotEmpty(dto.getMenuIds())) {
            roleMenuMapper.insertBatch(id, dto.getMenuIds());
        }
        return true;
    }

    @Override
    protected SysRole validateExistence(Long id) {
        SysRole role = baseMapper.selectById(id);
        Assert.notNull(role, () -> BizException.of(SystemConstant.ROLE_NOT_EXIST));
        return role;
    }

    @Override
    protected void validateUniqueness(SysRole role) {
        boolean existed = baseMapper.exists(lambdaQuery().getWrapper()
                .eq(SysRole::getRoleCode, role.getRoleCode())
                .eq(SysRole::getRoleName, role.getRoleName())
                .ne(role.getId() != null, SysRole::getId, role.getId()));
        Assert.isFalse(existed, () -> BizException.of(SystemConstant.ROLE_EXIST));
    }
}
